package com.atguigu.tingshu.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.client.AlbumClientFeign;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserClientFeign;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;


@Slf4j
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private AlbumClientFeign albumInfoFeignClient;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Autowired
    private UserClientFeign userInfoFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;






    @SneakyThrows
    @Override
    public Object channel(Long category1Id) {
        //根据一级分类id找出一级分类下的前七名三级分类列表
        List<BaseCategory3> topCategory3 = albumInfoFeignClient.findTopCategory3(category1Id);
        //将list转换为map
        Map<Long, BaseCategory3> collect = topCategory3.stream().collect(Collectors.toMap(
                key -> key.getId(),
                value -> value
        ));
        List<FieldValue> category3Id = topCategory3.stream().map(baseCategory3 -> {
            return FieldValue.of(baseCategory3.getId());
        }).collect(Collectors.toList());
        //new一个条件构造器
        SearchRequest.Builder builder = new SearchRequest.Builder();
        //查询的域
        builder.index("albuminfo_java1030");
        //构建查询条件
        builder.query(
                query -> query.terms(//词条查询
                        terms -> terms.field("category3Id")
                                .terms(//查询category1Id列
                                value -> value.value(category3Id)//根据category3Id查找
                        )
                )
        );
        //根据找到的数据进行分桶
        builder.aggregations(
                "aggCategory3Id",//取别名
                agg -> agg.terms(
                        terms -> terms.field("category3Id")//根据category3Id分桶
                ).aggregations(
                        "topHitsInfo",//取别名
                        subAgg -> subAgg.topHits(
                                topHits ->
                                        topHits.sort(//排序
                                                sort -> sort.field(//排序的列为hotScore。降序
                                                        field -> field.field("hotScore").order(SortOrder.Desc)
                                                )
                                        ).size(6)//取前6条数据
                        )
                )
        );
        SearchRequest build = builder.build();
        //执行查询，获取聚合数据
        SearchResponse<AlbumInfoIndex> search = elasticsearchClient.search(build, AlbumInfoIndex.class);
        //获取获取聚合数据
        Map<String, Aggregate> aggregations = search.aggregations();
        //对聚合对象非空判断
        if (aggregations != null) {
            //获取桶对象
            Aggregate aggregate = aggregations.get("aggCategory3Id");
            return aggregate.lterms().buckets().array().stream().map(longTermsBucket -> {
                JSONObject jsonObject = new JSONObject();
                //获取每个桶的id
                long bucketcategory3Id = longTermsBucket.key();
                //保存三级分类
                jsonObject.put("baseCategory3", collect.get(bucketcategory3Id));
                //获取子聚合。获取桶里面的数据，只有6个专辑数据
                Map<String, Aggregate> aggregationsMap = longTermsBucket.aggregations();
                if (aggregationsMap != null) {
                    //根据别名获取
                    Aggregate topHitsInfo = aggregationsMap.get("topHitsInfo");
                    List<AlbumInfoIndex> collectList = topHitsInfo.topHits().hits().hits().stream().map(album -> {
                        return album.source().to(AlbumInfoIndex.class);
                    }).collect(Collectors.toList());
                    jsonObject.put("list", collectList);
                }
                return jsonObject;
            }).collect(Collectors.toList());
        }
        return null;
    }

    /**
     * 查询一级分类下的 热度值前 10 的专辑
     * @param category1Id
     * @param hotScore
     * @return
     */
    @SneakyThrows
    @Override
    public Object findRankingList(Long category1Id , String hotScore) {
        return redisTemplate.opsForHash().get("Rank:"+category1Id,hotScore);
    }



    /**
     * 专辑详情
     * @param albumId
     * @return
     */
    @Override
    public Object albumInfo(Long albumId) {
        Map<String, Object> result = new ConcurrentHashMap<>();
        //异步编排，supplyAsync有返回值
        //获取专辑数据
        AlbumInfo albumInfo = albumInfoFeignClient.getAlbumInfo(albumId);
        if (albumInfo == null || albumInfo.getId() == null) {
            return null;
        }
        result.put("albumInfo", albumInfo);
        //获取分类数据
        //分类数据,一二三级分类
        result.put("baseCategoryView", albumInfoFeignClient.getBaseCategoryView(albumInfo.getCategory3Id()));
        //统计数据
        Map<String, Object> albumInfoStat = albumInfoFeignClient.getAlbumInfoStat(albumId);
        AlbumStatVo albumStatVo = new AlbumStatVo();
        albumStatVo.setAlbumId(albumId);
        //保存播放量
        albumStatVo.setPlayStatNum(Long.valueOf(albumInfoStat.get(SystemConstant.ALBUM_STAT_PLAY).toString()));
        //保存订阅量
        albumStatVo.setSubscribeStatNum(Long.valueOf(albumInfoStat.get(SystemConstant.ALBUM_STAT_SUBSCRIBE).toString()));
        //保存购买量
        albumStatVo.setBuyStatNum(Long.valueOf(albumInfoStat.get(SystemConstant.ALBUM_STAT_BROWSE).toString()));
        //保存评论数
        albumStatVo.setCommentStatNum(Long.valueOf(albumInfoStat.get(SystemConstant.ALBUM_STAT_COMMENT).toString()));
        result.put("albumStatVo", albumStatVo);
        //查询用户数据
        UserInfo userInfo = userInfoFeignClient.getUserInfo(albumInfo.getUserId());
        UserInfoVo userInfoVo = new UserInfoVo();
        //直接复制过去给userInfoVo
        userInfoVo.setId(albumInfo.getUserId());
        BeanUtils.copyProperties(userInfo, userInfoVo);
        result.put("announcer", userInfoVo);
        //等待所有线程执行完一起返回
//            CompletableFuture<Void> voidCompletableFuture =  CompletableFuture.allOf(Futrue1, Futrue2, Futrue3, Futrue4);

//            voidCompletableFuture.join();
        return result;
    }

    /**
     * 专辑搜索
     * @param albumIndexQuery
     * @return
     */
    @SneakyThrows
    @Override
    public Object seach(AlbumIndexQuery albumIndexQuery) {
        //查询构造器
        SearchRequest.Builder builder = new SearchRequest.Builder();
        //查询指定索引
        builder.index("albuminfo_java1030");
        //1、构建查询条件
        builder = buildSearchParames(builder,albumIndexQuery);
        //2、执行查询
        SearchResponse<AlbumInfoIndex> searchResponse =
                elasticsearchClient.search(builder.build(), AlbumInfoIndex.class);
        //3、解析结果
        AlbumSearchResponseVo vo = getSearchResult(searchResponse);
        //计算总页码
        vo.setPageNo(albumIndexQuery.getPageNo());
        Integer pageSize = albumIndexQuery.getPageSize();
        vo.setPageSize(pageSize);
        Long total = vo.getTotal();
        vo.setTotalPages(total%pageSize == 0? total/pageSize:total/pageSize+1);
        //4、返回结果
        return vo;
    }




    /**
     * 解析查询结果
     * @param searchResponse
     * @return
     */
    private AlbumSearchResponseVo getSearchResult(SearchResponse<AlbumInfoIndex> searchResponse) {
        //返回结果初始化
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
         //获取命中的数据
        HitsMetadata<AlbumInfoIndex> hits = searchResponse.hits();
        //获取数据总量
        long total = hits.total().value();
        albumSearchResponseVo.setTotal(total);
        //解析数据
        List<AlbumInfoIndexVo> list = hits.hits().stream().map(hit -> {
            //获取原始数据
            AlbumInfoIndex albumInfoIndex = hit.source();
            //获取高亮数据
            Map<String, List<String>> highlight = hit.highlight();
            if (highlight != null) {
                //获取高亮数据
                List<String> highlightlist = highlight.get("albumTitle");
                if (highlightlist != null) {
                    String albumTitle = highlightlist.stream().collect(Collectors.joining(","));
                    albumInfoIndex.setAlbumTitle(albumTitle);
                }
            }
            //返回结果初始化
            AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
            BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
            //返回
            return albumInfoIndexVo;
        }).collect(Collectors.toList());
        //保存
        albumSearchResponseVo.setList(list);
        return albumSearchResponseVo;
    }

    /**
     * 搜索条件
     * @param builder
     * @param albumIndexQuery
     * @return
     */
    private SearchRequest.Builder buildSearchParames(SearchRequest.Builder builder, AlbumIndexQuery albumIndexQuery) {
        //组合查询构造器
        BoolQuery.Builder boolSearchBuilder = new BoolQuery.Builder();
        //获取查询关键字
        String keyword = albumIndexQuery.getKeyword();
        if (StringUtils.isNotEmpty(keyword)){
            //组合查询
            boolSearchBuilder
                    //匹配查询，根据keyword查询字段albumTitle的匹配的数据
                    .should(should -> should.match(match -> match.field("albumTitle").query(keyword)))//标题查询
                    //匹配查询，根据keyword查询字段albumIntro的匹配的数据
                    .should(should -> should.match(match -> match.field("albumIntro").query(keyword)));//描述查询
        }
        //设置过滤条件: 一级分类
        Long category1Id = albumIndexQuery.getCategory1Id();
        if(category1Id != null){
            boolSearchBuilder.filter(filter->filter.term(
                    term->term.field("category1Id").value(category1Id)
            ));
        }
        //设置过滤条件: 二级分类
        Long category2Id = albumIndexQuery.getCategory2Id();
        if(category2Id != null){
            boolSearchBuilder.filter(filter->filter.term(
                    term->term.field("category2Id").value(category2Id)
            ));
        }
        //设置过滤条件: 三级分类
        Long category3Id = albumIndexQuery.getCategory3Id();
        if(category3Id != null){
            boolSearchBuilder.filter(filter->filter.term(
                    term->term.field("category3Id").value(category3Id)
            ));
        }
        //标签属性查询
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if(attributeList != null && attributeList.size() > 0){
            //遍历拼接条件
            attributeList.stream().forEach(attribute->{
                //切分: 0-属性id must 1-属性值id
                String[] split = attribute.split(":");
                //添加条件
                boolSearchBuilder.filter(
                        filter->filter.nested(
                                nested->nested
                                        .path("attributeValueIndexList")
                                        .query(query->query.bool(
                                                bool->bool
                                                        .must(m->m.term(t->t.field("attributeValueIndexList.attributeId").value(split[0])))//名称id相等
                                                        .must(m->m.term(t->t.field("attributeValueIndexList.valueId").value(split[1])))//属性值id相等
                                        ))
                        )
                );
            });
        }
        //分页
        Integer pageNo = albumIndexQuery.getPageNo();
        Integer pageSize = albumIndexQuery.getPageSize();
        //从哪一行开始;从0行开始
        builder.from((pageNo-1)*pageSize);
        //显示多少条数据；显示20条数据
        builder.size(pageSize);
        //设置高亮条件
        builder.highlight(highlight -> highlight.fields("albumTitle",h->h.preTags("<font style=color:red>").postTags("</font>")));
        //保存查询条件
        builder.query(boolSearchBuilder.build()._toQuery());
        //分页: 1:desc-综合 2:desc-播放量 3:desc-发布时间
        String order = albumIndexQuery.getOrder();
        if(StringUtils.isNotEmpty(order)){
            //0-域 1-规则
            String[] split = order.split(":");
            switch (split[0]){
                //综合
                case "1"->builder.sort(s->s.field(f->f.field("id").order(SortOrder.Desc)));
                //播放量
                case "2"->builder.sort(s->s.field(f->f.field("playStatNum").order(SortOrder.Desc)));
                //发布时间
                case "3"->builder.sort(s->s.field(f->f.field("createTime").order(SortOrder.Desc)));
            }
        }else{
            builder.sort(s->s.field(f->f.field("hotScore").order(SortOrder.Desc)));
        }
        return builder;
    }
}
